<?php

namespace Dongasai\WkProtoPhp\Tests\PHPUnit\Binary;

use Dongasai\WkProtoPhp\Protocol;
use Dongasai\WkProtoPhp\Enums\FrameType;
use PHPUnit\Framework\TestCase;

/**
 * 二进制文件测试基类
 */
abstract class BinaryTestCase extends TestCase
{
    protected Protocol $protocol;
    protected string $binaryDir;
    protected string $filename;
    protected string $expectedPacketClass;
    protected int $expectedFrameType;

    protected function setUp(): void
    {
        $this->protocol = new Protocol();
    }

    /**
     * 测试二进制文件存在性
     */
    public function testBinaryFileExists(): void
    {
        $binaryFile = $this->getBinaryFilePath();
        $this->assertFileExists($binaryFile, "Binary file should exist: {$binaryFile}");
        $this->assertGreaterThan(0, filesize($binaryFile), "Binary file should not be empty: {$binaryFile}");
    }

    /**
     * 测试二进制文件基本解析
     */
    public function testBinaryFileBasicParsing(): void
    {
        $binaryFile = $this->getBinaryFilePath();
        $binaryData = file_get_contents($binaryFile);
        
        $this->assertIsString($binaryData);
        $this->assertGreaterThan(0, strlen($binaryData));
        
        // 解析帧头
        $typeAndFlags = ord($binaryData[0]);
        $frameType = ($typeAndFlags >> 4) & 0x0F;
        
        $this->assertEquals($this->expectedFrameType, $frameType, 
            "Frame type should match expected: {$this->expectedFrameType}");
    }

    /**
     * 测试协议解码
     */
    public function testProtocolDecode(): void
    {
        $binaryFile = $this->getBinaryFilePath();
        $binaryData = file_get_contents($binaryFile);
        
        [$packet, $length, $error] = $this->protocol->decodeFrame($binaryData);
        
        if ($error !== null) {
            // 如果解码失败，验证错误类型
            $this->assertInstanceOf(\RuntimeException::class, $error);
            $this->markTestIncomplete("Binary format compatibility issue: " . $error->getMessage());
            return;
        }
        
        $this->assertNotNull($packet, "Should decode packet from {$this->filename}");
        $this->assertInstanceOf($this->expectedPacketClass, $packet, 
            "Should decode to expected class: {$this->expectedPacketClass}");
        $this->assertGreaterThan(0, $length, "Should consume some bytes for {$this->filename}");
        
        if (method_exists($packet, 'getFrameType')) {
            $this->assertEquals($this->expectedFrameType, $packet->getFrameType(), 
                "Packet frame type should match expected");
        }
    }

    /**
     * 测试编码解码一致性（如果解码成功）
     */
    public function testEncodeDecodeConsistency(): void
    {
        $binaryFile = $this->getBinaryFilePath();
        $binaryData = file_get_contents($binaryFile);
        
        [$packet, $length, $error] = $this->protocol->decodeFrame($binaryData);
        
        if ($error !== null) {
            // 如果解码失败，跳过这个测试
            $this->markTestSkipped("Cannot test consistency due to decode error");
            return;
        }
        
        // 重新编码
        $encoded = $this->protocol->encodeFrame($packet);
        $this->assertIsString($encoded);
        $this->assertGreaterThan(0, strlen($encoded));
        
        // 重新解码
        [$redecodedPacket, $redecodedLength, $redecodedError] = $this->protocol->decodeFrame($encoded);
        
        $this->assertNull($redecodedError, "Should re-decode without error");
        $this->assertInstanceOf($this->expectedPacketClass, $redecodedPacket, 
            "Should re-decode to expected class");
    }

    /**
     * 获取二进制文件路径
     */
    protected function getBinaryFilePath(): string
    {
        return __DIR__ . '/../../../example/' . $this->binaryDir . '/' . $this->filename;
    }

    /**
     * 获取包类型描述
     */
    protected function getPacketTypeDescription(): string
    {
        try {
            return FrameType::from($this->expectedFrameType)->toString();
        } catch (\ValueError $e) {
            return "Unknown[{$this->expectedFrameType}]";
        }
    }

    /**
     * 测试文件基本信息
     */
    public function testBinaryFileInfo(): void
    {
        $binaryFile = $this->getBinaryFilePath();
        $fileSize = filesize($binaryFile);
        $binaryData = file_get_contents($binaryFile);
        
        // 验证文件存在且不为空
        $this->assertFileExists($binaryFile);
        $this->assertGreaterThan(0, $fileSize);
        
        // 验证文件大小合理
        $this->assertLessThan(1024, $fileSize, 'Binary file should be less than 1KB');
        
        // 验证二进制数据格式正确
        $this->assertNotEmpty($binaryData);
    }
}